package zpe.controledeacesso.service;


import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import zpe.controledeacesso.model.bean.GrupoDeUsuario;
import zpe.controledeacesso.model.bean.Modulo;
import zpe.controledeacesso.model.bean.NivelAcesso;
import zpe.controledeacesso.model.bean.Permissoes;
import zpe.genericcrud.exception.ApplicationException;
import zpe.genericcrud.service.GenericService;


@SuppressWarnings({ "unchecked", "rawtypes" })

@Service
public class GrupoDeUsuariosService extends GenericService<GrupoDeUsuario> {
	/**
	 * 
	 */
	private static final long serialVersionUID = -2044749729931423290L;
	
	@Autowired private PermissoesService servicePermissoes;
	@Autowired private TransacaoService serviceTransacao;

	/**
	 * @return the servicePermissoes
	 */
	public PermissoesService getServicePermissoes() {
		return servicePermissoes;
	}

	/**
	 * @param servicePermissoes the servicePermissoes to set
	 */
	public void setServicePermissoes(PermissoesService servicePermissoes) {
		this.servicePermissoes = servicePermissoes;
	}

	/**
	 * @return the serviceTransacao
	 */
	public TransacaoService getServiceTransacao() {
		return serviceTransacao;
	}

	/**
	 * @param serviceTransacao the serviceTransacao to set
	 */
	public void setServiceTransacao(TransacaoService serviceTransacao) {
		this.serviceTransacao = serviceTransacao;
	}
	
	/**
	 * Consulta todos os grupos de um módulo abaixo do nível passao. Caso seja Master, pega todos os grupos do módulo.
	 * @param nivel
	 * @param modulo
	 * @return
	 */
	public List<GrupoDeUsuario> findByNivel(NivelAcesso nivel, int modulo ) {
		DetachedCriteria c = DetachedCriteria.forClass(GrupoDeUsuario.class);
		if( nivel.ordinal() < NivelAcesso.MASTER.ordinal() ){
			c.add(Restrictions.lt( "nivelDeAcesso", nivel ));
		}
		c.add(Restrictions.eq("modulo.id", modulo ));
		c.addOrder( Order.asc( "descricao" ) );
		List<GrupoDeUsuario> list = getDao().getEntityManager().findByCriteria(c);
		return list;
	}
	
	public List<GrupoDeUsuario> findByNivel(GrupoDeUsuario grupo) {
		DetachedCriteria c = DetachedCriteria.forClass(GrupoDeUsuario.class);
		
		if(StringUtils.hasText(grupo.getDescricao()))
			c.add(Restrictions.ilike("descricao", grupo.getDescricao(), MatchMode.ANYWHERE));
		
		if( grupo.getNivelDeAcesso().ordinal() < NivelAcesso.MASTER.ordinal() ){
			c.add(Restrictions.lt( "nivelDeAcesso", grupo.getNivelDeAcesso() ));
		}
		
		c.add(Restrictions.eq("modulo.id", grupo.getModulo().getId() ));
		c.addOrder( Order.asc( "descricao" ) );
		List<GrupoDeUsuario> list = getDao().getEntityManager().findByCriteria(c);
		return list;
	}
	
	public List<GrupoDeUsuario> findByModulo(Modulo modulo) {
		DetachedCriteria c = DetachedCriteria.forClass(GrupoDeUsuario.class);
		c.add(Restrictions.eq("modulo.id", modulo.getId()));
		c.addOrder( Order.asc( "descricao" ) );
		List<GrupoDeUsuario> list = getDao().getEntityManager().findByCriteria(c);
		return list;
	}
	
	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class )
	public GrupoDeUsuario insert(GrupoDeUsuario obj, List<Permissoes> permissoes) {
		
		verificarGrupoExistente(obj);
		
		obj.setGrupoTransacao( null );
		super.insert(obj);
		getServicePermissoes().insertAll( permissoes );
		return obj;	
	}
	
	public void verificarGrupoExistente(GrupoDeUsuario gu) {
		
		Integer id = gu.getId();
		
		if (id == null) {
			id = -1;
		}
		
		Object [] params = new Object[] { gu.getModulo().getId(), gu.getDescricao() , id, id};
		
		List _result = getDao().getEntityManager().findByNamedQuery("grupoUsuario.byModuloAndDescricao", params);
		
		if(!CollectionUtils.isEmpty(_result)) {
			throw new ApplicationException("Grupo de Usuário já cadastrado para este módulo.");
		}
	}
	
	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class )
	public GrupoDeUsuario update(GrupoDeUsuario obj, List<Permissoes> permissoes) {
		
		verificarGrupoExistente(obj);
		
		obj.setGrupoTransacao( null );
		super.update(obj);
		List<Integer> itens = new ArrayList<Integer>();
		for( Permissoes p : permissoes ){
			p = getServicePermissoes().update( p );
			itens.add( p.getId() );
		}
		final int grupo = obj.getId();
		final List<Integer> lista = itens;
		HibernateCallback callback = new HibernateCallback(){
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				String hql = "delete from Permissoes where grupoDeUsuario.id = :grupo ";
				if( !lista.isEmpty() )
					hql = hql +  " and id not in (:id)";
				
				Query q = session.createQuery( hql );
				
				q.setParameter("grupo", grupo );
				if( !lista.isEmpty() )
					q.setParameterList("id", lista );
							
				return q.executeUpdate();
			}
		};
		
		getDao().getEntityManager().execute(callback);	
		return obj;
	}
}